package cn.mystylefree.sqlSession;

import cn.mystylefree.po.Configuration;
import cn.mystylefree.po.MappedStatement;

import java.lang.reflect.*;
import java.util.List;

/**
 * @program my-project-model
 * @description:
 * @author: lee
 * @create: 2022/06/15 09:27
 */
public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * 这里其实就能够直接写jdbc了，但是为了更优雅继续进行封装
     *
     * @param statementId 这里是需要将查询的唯一标识传进来，
     *                    只有传递这个id了才知道执行的sql语句（Configuration中查出来）
     * @param params      查询条件
     * @param <E>
     * @return
     */
    @Override
    public <E> List<E> selectList(String statementId, Object... params) {
        //将要完成SimpleExecutor里面的query方法的调用
        SimpleExecutor simpleExecutor = new SimpleExecutor();

        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<E>) list;
    }

    /**
     * 查询一条，可以使用查询列表的方法
     *
     * @param statementId
     * @param params
     * @param <T>
     * @return
     */
    @Override
    public <T> T selectOne(String statementId, Object... params) {
        List<Object> objects = this.selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者查询结果过多");

        }
    }

    /**
     * 使用JDK动态代理来为Dao 接口生成代理对象，并返回
     *
     * @param mapperClass
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //底层还是调用JDBC
                //实现：根据不同的情况，来调用selectOne或者selectList

                //准备参数 1 statementId ：sql语句的唯一标识 namespace.id :接口权限定名.方法名
                // 因为这里获取不到配置文件中的配置，所以在配置的时候需要遵循特定的限制
                //namespace 接口的 cn.mystylefree.my_persistence_demo.dao.IUserDao
                //id是方法名  findAll  findByCondition
                //2 params

                /*参数准备*/
                //方法名 findAll
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String statementId = className + "." + methodName;
                //params 实际传递的参数 args传递


                //有的返回值并不是List只是一个对象
                //所以对返回结果的类型进行判断 是List还是对象
                /*获取被调用方法的返回值类型*/

                Type genericReturnType = method.getGenericReturnType();
                //判断是否进行了 泛型类型的参数化
                if (genericReturnType instanceof ParameterizedType) {
                    //如果是泛型化 就是List集合
                    List<Object> selectList = selectList(statementId, args);
                    return selectList;
                }
                //返回值类型是 对象
                return selectOne(statementId, args);
            }
        });
        return (T) proxyInstance;
    }
}
